from random import random

from pygame import Surface
from pygame.display import flip
from pygame.draw import line, rect

from ..ess import typeof
from ..pylib.game import getWindow, waiting, waitQuit

dpos1 = ((0, 1), (1, 0), (0, -1), (-1, 0))
dpos2 = ((1, 1), (1, -1), (-1, -1), (-1, 1))
dpos = dpos1 + dpos2


class Cell:
    def __init__(self):
        self.tag: dict = dict()
        self.life: bool = False

    def __getitem__(self, index):
        return self.tag.get(index, None)

    def __setitem__(self, index, value):
        self.tag[index] = value

    def setoalive(self):
        self.life = True
        return self


class LifeGame:
    def __init__(self, size: tuple, lencell: int = 10):
        self.size = self.width, self.height = size
        self.board: dict[tuple, Cell] = {}
        self.lencell = lencell
        for i in range(self.width):
            for j in range(self.height):
                self.board[i, j] = Cell()
        self.reseTag()

    def __getitem__(self, index):
        return self.board.get(index, Cell())

    def __setitem__(self, index, value):
        self.board[index] = value

    def __str__(self):
        ret = ''
        for j in range(self.height):
            for i in range(self.width):
                ret += '#' if self[i, j].life else ' '
            ret += '\n' if j < self.height - 1 else ''
        return ret

    def __repr__(self):
        return f"{typeof(self)}(size={self.size})"

    def reseTag(self) -> None:
        board = self.board
        for i in range(self.width):
            for j in range(self.height):
                board[i, j].tag = {
                    'x': i,
                    'y': j,
                }

    def next(self) -> None:
        nextBoard = {}
        for i in range(self.width):
            for j in range(self.height):
                cell = Cell()
                count = 0
                for dx, dy in dpos:
                    if self[i + dx, j + dy].life:
                        count += 1
                if count == 3:
                    cell.life = True
                elif count == 2:
                    cell.life = self[i, j].life
                else:
                    cell.life = False
                nextBoard[i, j] = cell
        self.board = nextBoard

    def changeProportionally(self, percent) -> None:
        probability = abs(percent)
        seTo = True if percent / probability > 0 else False
        for i in range(self.width):
            for j in range(self.height):
                if random() < probability:
                    self[i, j].life = seTo

    def drawgrad(self, sur: Surface, col=(0, 0, 0, 255)) -> None:
        le = self.lencell
        for i in range(1, self.width):
            line(sur, col, (i * le, 0), (i * le, self.height * le))
        for j in range(1, self.height):
            line(sur, col, (0, j * le), (self.width * le, j * le))

    def drawbg(self, sur: Surface, style: "dict[str,tuple]") -> None:
        le = self.lencell
        sur.fill(style["dead"])
        col = style["alive"]
        for i in range(self.width):
            for j in range(self.height):
                if self[i, j].life:
                    rect(sur, col, (i * le, j * le, le, le))

    def draw(self, sur: Surface, style: "dict[str,tuple]") -> None:
        self.drawbg(sur, style)
        self.drawgrad(sur)
        flip()

    def toimg(self, style: "dict[str,tuple]") -> None:
        window = getWindow(
            (self.width * self.lencell, self.height * self.lencell), repr(self)
        )
        self.draw(window, style)
        waiting()
